package main.app;

/**
 * Created by hdt3213 on 17/3/30.
 */

import main.base.*;
import main.model.*;

import main.model.Group;
import org.json.JSONObject;

import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class ChatApp implements TcpApp {
	private TcpServer server;
	private DB db = new DB();
	private HashMap<String, String> session;

	private static HashMap<String, User> onlineUser = new HashMap<String, User>(); //  address: user
	private static HashMap<String, String> userAddress = new HashMap<String, String>(); //  uuid: address
	private static HashMap<String, FileRequest> fileRequests = new HashMap<String, FileRequest>();

	final static String ERROR_INTERNAL_SERVER = "{\"status\": \"ERROR_INTERNAL_SERVER\"}";
	final static String ERROR_PERMISSION_DENIED =  "{\"status\": \"ERROR_PERMISSION_DENIED\"}";

	public void setup() {
		try {
			db.connect();
			db.prepare();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void setServer(TcpServer server) {
		this.server = server;
	}

	@Override
	public void onMsg(String address, String msg) {
		try {
			JSONObject request = new JSONObject(msg);
			String action = request.getString("action");
			switch (action) {
				case "account/register":
					registerViews(address, request);
					break;
				case "account/login":
					loginViews(address, request);
					break;
				case "chat/send":
					chatSendViews(address, request);
					break;
				// todo
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}


	void registerViews(String address, JSONObject request) throws IOException {

		try {
			HashMap<String, String> response = new HashMap<String, String>();

			String name = request.getString("name");
			String password = request.getString("password");
			int face = request.getInt("face");

			User user = new User();

			user.setName(name);
			user.setPassword(password);
			user.setFace(face);
			String uuid = db.insertUser(user);

			response.put("status", "ok");
			response.put("uid", uuid);
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}

	}

	void loginViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			String uuid = request.getString("uid");
			User user = db.getUser(uuid);

			// forbid login duplicate
			onlineUser.put(address, user);
			userAddress.put(user.getUuid(), address);

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void chatSendViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			String dest = userAddress.get(request.getString("dest"));
			HashMap<String, String> push = new HashMap<String, String>();

			push.put("event", "chat/msg");
			push.put("msg", request.getString("msg"));
			server.send(dest, jsonify(push));

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address, ERROR_INTERNAL_SERVER);
		}
	}

	void fileRequestViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			HashMap<String, String> push = new HashMap<String, String>();

			String fid = DB.getUUID(request);
			String dest = userAddress.get(request.getString("dest"));
			User operator = onlineUser.get(address);
			FileRequest fileRequest = new FileRequest();

			push.put("event", "file/request");
			push.put("filename", request.getString("filename"));
			push.put("size", String.valueOf(request.getInt("size")));
			push.put("sender", operator.getUuid());
			push.put("fid", fid);
			server.send(dest, jsonify(push));

			fileRequest.fid = fid;
			fileRequest.sender = operator.getUuid();
			fileRequest.receiver = request.getString("dest");
			fileRequest.filename = request.getString("filename");
			fileRequest.size = request.getInt("size");
			fileRequests.put(fid, fileRequest);

			response.put("status", "ok");
			response.put("fid", fid);
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address, ERROR_INTERNAL_SERVER);
		}
	}

	void fileResponseViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			HashMap<String, String> push = new HashMap<String, String>();
			String fid = request.getString("fid");

			FileRequest fileRequest = fileRequests.get(fid);
			String sender = userAddress.get(fileRequest.sender);

			push.put("event", "file/response");
			push.put("fid", fid);
			push.put("permission", String.valueOf(request.getInt("permission")));
			server.send(sender, jsonify(response));

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void createGroupViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);
			String name = request.getString("name");
			int face = request.getInt("face");

			String gid = db.createGroup(operator.getUuid(), name, face);

			response.put("status", "ok");
			response.put("gid", gid);
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void editGroupViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			Group group = db.getGroup(gid);

			int role = db.getRole(operator.getUuid(), gid);
			if (role < 2) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			if (request.has("name")) {
				group.setName(request.getString("name"));
			}
			if (request.has("face")) {
				group.setFace(request.getInt("face"));
			}
			db.updateGroup(group);

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void joinGroupViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);


			String gid = request.getString("gid");
			Group group = db.getGroup(gid);

			db.join(operator.getUuid(), gid, Group.member);

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void quitGroupViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");


			db.quit(operator.getUuid(), gid);

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void forbidMemberViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String uid = request.getString("uid");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			int role = db.getRole(operator.getUuid(), gid);
			if (role < Group.manager) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			db.assign(uid, gid, Group.forbidden);

			pushAddress = userAddress.get(uid);
			if (address != null) {
				push.put("event", "group/forbid");
				push.put("gid", gid);
				server.send(pushAddress, jsonify(push));
			}

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void expellMemberViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String uid = request.getString("uid");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			int role = db.getRole(operator.getUuid(), gid);
			if (role < Group.manager) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			db.quit(uid, gid);

			pushAddress = userAddress.get(uid);
			if (address != null) {
				push.put("event", "group/expel");
				push.put("gid", gid);
				server.send(pushAddress, jsonify(push));
			}

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void allowMemberViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String uid = request.getString("uid");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			int role = db.getRole(operator.getUuid(), gid);
			if (role < Group.manager) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			db.assign(uid, gid, Group.member);

			pushAddress = userAddress.get(uid);
			if (address != null) {
				push.put("event", "group/allow");
				push.put("gid", gid);
				server.send(pushAddress, jsonify(push));
			}

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void assignViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String uid = request.getString("uid");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			int role = db.getRole(operator.getUuid(), gid);
			if (role < Group.creator) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			db.assign(uid, gid, Group.manager);

			pushAddress = userAddress.get(uid);
			if (address != null) {
				push.put("event", "group/assign");
				push.put("gid", gid);
				server.send(pushAddress, jsonify(push));
			}

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void dismissViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String uid = request.getString("uid");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			int role = db.getRole(operator.getUuid(), gid);
			if (role < Group.creator) {
				server.send(address, ERROR_PERMISSION_DENIED);
			}

			db.assign(uid, gid, Group.member);

			pushAddress = userAddress.get(uid);
			if (address != null) {
				push.put("event", "group/dismiss");
				push.put("gid", gid);
				server.send(pushAddress, jsonify(push));
			}

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address,ERROR_INTERNAL_SERVER);
		}
	}

	void groupSendViews(String address, JSONObject request) throws IOException {
		try {
			HashMap<String, String> response = new HashMap<String, String>();
			User operator = onlineUser.get(address);

			String gid = request.getString("gid");
			String msg = request.getString("msg");

			HashMap<String, String> push = new HashMap<String, String>();
			String pushAddress;

			push.put("event", "group/msg");
			push.put("gid", "group/msg");
			push.put("sender", operator.getUuid());
			push.put("msg", msg);
			groupBoardcast(gid, jsonify(push));

			response.put("status", "ok");
			server.send(address, jsonify(response));
		}
		catch (Exception e) {
			e.printStackTrace();
			server.send(address, ERROR_INTERNAL_SERVER);
		}
	}

	void groupBoardcast(String gid, String msg) throws SQLException, IOException {
		String[] members = db.getMembers(gid);
		String address;
		for (String uuid : members) {
			address = userAddress.get(uuid);
			if (address == null) {
				continue;
			}

			server.send(address, msg);
		}
	}

	@Override
	public void onClose(String address) {
		try {
			User user = onlineUser.get(address);
			onlineUser.remove(address);
			userAddress.remove(user.getUuid());
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String jsonify(Map map) {
		return (new JSONObject(map)).toString();
	}
}